package paper.http;

import java.io.*;
import java.util.HashMap;
import java.util.regex.*;

import paper.batch.Batch;
import paper.batch.MD5;

public class UpLoad {
    private InputStream inputStream = null;

    /**
     * Constructor that creates a new uploader for the inputStream of this
     * specific servlet.
     * @param is The inputStream to read the data from.
     */
    public UpLoad(InputStream is) {
        inputStream = is;
    }

    static class FileParameter {
        File file; String original; MD5 md5; String md5String;
        FileParameter(File f, String o) {
            this.file = f; this.original = o; this.md5 = new MD5();
        }
    }

    private HashMap<String,String> parameters = new HashMap<String,String>();
    private HashMap<String,FileParameter> files = new HashMap<String,FileParameter>();

    /**
     * Defines a field in the form for which we expect a STRING value
     * @param name The name of the field
     */
    public void fieldString(String name) {
        parameters.put(name,"");
    }

    /**
     * Defines a field in the form for which we expect a File value
     * @param name The name of the field
     */
    public void fieldFile(String name) throws IOException {
        files.put(name,new FileParameter(Batch.createTempFile(),null));
    }
    
    private boolean matches(byte[] buffer, int i, byte[] match) {
        for (int k = 0; k < match.length; k++) {
            if (buffer[i+k] != match[k]) {
                return false;
            }
        }
        return true;
    }

    public String strip(String x) {
        if (x.charAt(0) == '\'' || x.charAt(0) == '"') {
            return x.substring(1,x.length()-1);
        }
        return x;
    }

    static int lll = 120;
    /**
     * This method causes all the data to be read from the input stream and
     * to be stored in a variety of files. The files are stored in the temp
     * directory as pointed to by the Batch class. This could be a
     * parameter of upLoad. This method throws an IOException if it does
     * not like the inputStream.
     */
    public void process() throws IOException {        
        StreamReader sr = new StreamReader(inputStream, lll++);
        byte[] separatorLine = sr.getLine();
        byte[] endLine = new byte[separatorLine.length+2];
        System.arraycopy(separatorLine,0,endLine,0,separatorLine.length-2);
        System.arraycopy("--\r\n".getBytes(),0,endLine,endLine.length-4,4);
        Pattern pfile = Pattern.compile("filename=('[^']*'|\"[^\"]*\"|[^ \r\n;]*)");
        Pattern pname = Pattern.compile("\\bname=('[^']*'|\"[^\"]*\"|[^ \r\n;]*)");
        boolean atEnd = false;

        do {
            String original = "";
            String fieldName = "";
            String line = "";
            while (true) {
                sr.moveToNextLine();
                line = new String(sr.getLine());
//                System.out.println("Header >" + line + "<");
                if (line.equals("\r\n\r\n")) {
                    break;
                }
                Matcher m = pfile.matcher(line);
                if (m.find()) {
                    original = strip(m.group(1));
                }
                m = pname.matcher(line);
                if (m.find()) {
                    fieldName = strip(m.group(1));
                }
            }
            sr.skip(4);      // jump over CRLFCRLF
            String stringParameter = parameters.get(fieldName) ;
            OutputStream stringOutput = null;
            FileParameter oFile = files.get(fieldName);
            if (stringParameter == null) {
                if (oFile != null) {
                    oFile.original = original;
                    sr.setOutputStream(new BufferedOutputStream(new FileOutputStream(oFile.file)), oFile.md5);
                }
            } else {
                stringOutput = new ByteArrayOutputStream();
                sr.setOutputStream(stringOutput, null);
            }
            while (true) {
                sr.moveToNextLine();
                if (sr.lineEquals(separatorLine)) {
                    break;
                }
                if (sr.lineEquals(endLine)) {
                    atEnd = true;
                    break;
                }
            }
            sr.setOutputStream(null, null);
            if (stringParameter != null) {
                parameters.put(fieldName,stringOutput.toString());
            } else {
                oFile.md5String = oFile.md5.digest();
            }
      } while (!atEnd);
    }

    /**
     * Gets the field value assuming it is a string.
     * @param x The field name
     * @returns a String which is the field value
     */
    public String getParameter(String x) {
        return parameters.get(x);
    }


    /**
     * Gets the Original
     * @param x The field name
     * @returns a String which is the field value
     */
    public String getOriginal(String x) {
        return getFilePar(x).original;
    }

    /**
     * Gets the File associated with an uploaded file,
     * @param x The field name
     * @returns the File.
     */
    public File getFile(String x) {
        return getFilePar(x).file;
    }

    /**
     * Gets the length of a file that has been uploaded
     * @param x The field name
     * @returns a long whihc is the file length in bytes
     */
    public long getFileLength(String x) {
        return getFilePar(x).file.length();
    }


    /**
     * Gets the md5 checksum of a file parameter
     * @param x The field name
     * @returns a String which is the md5 value, base 16
     */
    public String getMd5(String x) {
        return getFilePar(x).md5String;
    }

    private FileParameter getFilePar(String x) {
        return files.get(x);        
    }
}
